package com.ouyunc.web.webmvc;


import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.ouyunc.common.constant.Auth2Constant;
import com.ouyunc.web.annotation.CurrentLoginUser;
import com.ouyunc.web.common.LoginUser;
import com.ouyunc.web.exception.CustomerException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.MethodParameter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URLDecoder;
import java.util.List;

/**
 * @Author fangzhenxun
 * @Description: 当前登录用户的
 * WebMvcConfigurerAdapter已过时
 * 在jdk1.8后增加了default，接口中的方法不必被实现类全部实现，所以直接实现WebMvcConfigurer即可，不需要通过抽象类WebMvcConfigurerAdapter来过渡。
 * @Version V1.0
 **/
@Slf4j
@Configuration
public class CurrentLoginUserConfigurer implements WebMvcConfigurer {


    /**
     * 添加自定义转换器，将返回JSON中的long 类型转string
     * @param converters
     */
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
        ObjectMapper objectMapper = new ObjectMapper();
        SimpleModule simpleModule = new SimpleModule();
        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
        simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
        objectMapper.registerModule(simpleModule);
        jackson2HttpMessageConverter.setObjectMapper(objectMapper);
        converters.add(0, jackson2HttpMessageConverter);
    }

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        // 注册currentLoginUserMethodArgumentResolverHandlerr的参数分解器
        resolvers.add(currentLoginUserMethodArgumentResolverHandler());
    }


    /**
     * @param
     * @return com.ouyunc.common.config.webmvc.handler.CurrentLoginUserMethodArgumentResolverHandler
     * @Author fangzhenxun
     * @Description
     */
    @Bean
    public HandlerMethodArgumentResolver currentLoginUserMethodArgumentResolverHandler() {
        return new HandlerMethodArgumentResolver() {
            /**
             * @Author fangzhenxun
             * @Description 判断是否支持使用@CurrentLoginUser注解的参数
             * @param methodParameter
             * @return boolean
             */
            @Override
            public boolean supportsParameter(MethodParameter methodParameter) {
                //如果该参数注解有@CurrentLoginUser且参数类型是LoginUser或其子类,那么就放行（调用下面resolveArgument处理参数方法）
                return methodParameter.hasParameterAnnotation(CurrentLoginUser.class) && methodParameter.getParameterType().isAssignableFrom(LoginUser.class);
            }


            /**
             * @Author fangzhenxun
             * @Description 真正解析处理的地方
             * @param methodParameter
             * @param modelAndViewContainer
             * @param webRequest
             * @param webDataBinderFactory
             * @return java.lang.Object  这个object 就是LoginUser实体类
             */
            @Override
            public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest webRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
                // 1.直接从请求头读取用户信息
                String loginUserPrinciple = webRequest.getHeader(Auth2Constant.HEADER_PRINCIPLE);
                // 如果从请求头中拿到当前登录人的信息，则直接返回，否从从redis中取
                if (StringUtils.isNotBlank(loginUserPrinciple)) {
                    return (LoginUser) () -> {
                        try {
                            return JSON.parseObject(URLDecoder.decode(loginUserPrinciple, Auth2Constant.UTF_8));
                        } catch (UnsupportedEncodingException e) {
                            e.printStackTrace();
                            // 打印错误日志
                            log.error("当前的登录人转换异常！");
                        }
                        throw new CustomerException("当前的登录人转换异常！");
                    };
                }
                // 2.从请求头拿到token，从redis读取用户信息
                // 从请求头获取token
//                String accessToken = webRequest.getHeader(Auth2Constant.AUTHORIZATION);
//                if (StringUtils.isNotBlank(accessToken) && accessToken.toLowerCase().startsWith(Auth2Constant.BEARER_TYPE.toLowerCase())) {
//                    accessToken = accessToken.substring(Auth2Constant.BEARER_TYPE.length()).trim();
//                    // 根据token从缓存中取出凭证
//                    String redisPrefix = "default-oauth2-token:";
//                    String redisMid = "principle:";
//                    String currentAuthentication = readAuthentication(redisPrefix + redisMid, accessToken);
//                    return (LoginUser) () -> JSON.parseObject(currentAuthentication);
//                }
                // 3.从参数拿到token，从redis读取用户信息
                // @TODO 当然这里也可以从路径上取出token，具体规则可以定：webRequest.getParameter("token")
                // 4.从jwt中解析token，读取用户信息
                // 5.获取用户登录唯一标识，查询数据库获取登录信息
                log.error("当前登录失效！");
                throw new CustomerException("当前登录失效！");
            }
        };
    }


    /**
     * @param redisPrefix
     * @param token
     * @return java.lang.Object
     * @Author fangzhenxun
     * @Description 获取认证成功后的用户信息
     */
//    public String readAuthentication(String redisPrefix, String token) {
//        byte[] bytes = null;
//        RedisConnection conn = lettuceConnectionFactory.getConnection();
//        try {
//            bytes = conn.get(STRING_SERIALIZER.serialize(redisPrefix + token));
//        } finally {
//            conn.close();
//        }
//        return STRING_SERIALIZER.deserialize(bytes);
//    }
}
